package com.echo.im.domain.friend.service;


import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.echo.im.client.IMClient;
import com.echo.im.client.model.IMMessage;
import com.echo.im.client.model.IMTerminalType;
import com.echo.im.client.model.IMUserInfo;
import com.echo.im.common.contant.Command;
import com.echo.im.common.enums.MessageStatus;
import com.echo.im.common.enums.MessageType;
import com.echo.im.common.exception.GlobalException;
import com.echo.im.common.result.ResultEnum;
import com.echo.im.common.util.JwtUtil;
import com.echo.im.common.vo.PrivateMessageVO;
import com.echo.im.domain.friend.dto.AddFriendDTO;
import com.echo.im.domain.friend.dto.HandlerAddFriendDTO;
import com.echo.im.domain.friend.dto.UpdateFriendDTO;
import com.echo.im.domain.friend.manager.FriendManager;
import com.echo.im.domain.friend.vo.ApplyFriendVO;
import com.echo.im.domain.friend.vo.FriendVO;
import com.echo.im.domain.friend.vo.HandlerFriendApplyVO;
import com.echo.im.domain.user.manager.UserManager;
import com.echo.im.domain.user.vo.OnlineTerminalVO;
import com.echo.im.domain.user.vo.UserVO;
import com.echo.im.infrastructure.generator.IdGenerator;
import com.echo.im.infrastructure.session.SessionContext;
import com.echo.im.infrastructure.session.UserSession;
import com.echo.im.infrastructure.temp.ApplyFriendTemp;
import com.echo.im.repository.dao.entity.Friend;
import com.echo.im.repository.dao.entity.User;
import com.echo.im.repository.dao.mapper.FriendMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lxy
 * @date 2024-08-14
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> implements FriendService {

    private final FriendManager friendManager;
    private final UserManager userManager;
    private final IMClient imClient;
    private final IdGenerator idGenerator;

    /**
     * 更新好友信息
     *
     * @param dto 包含需要更新的好友信息的实体
     */
    @Override
    public void update(UpdateFriendDTO dto) {
        long userId = SessionContext.getSession().getUserId();

        friendManager.updateFriendData(userId, dto.getId(), dto.getNickName(), dto.getHeadImage());
    }

    /**
     * 删除好友
     * 1. 删除数据库
     * 2. 给自己的其他端发送信息
     * 3. 在每个端删除好友历史记录
     * 4. 不提醒被删除的好友，模拟微信，悄悄删除
     * 5. 当被删除方查询删除方的好友信息时，显示不是好友，或者当其发送消息的时候，返回红色感叹号
     *
     * @param friendId 好友的ID
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delFriend(Long friendId) {
        UserSession session = SessionContext.getSession();

        friendManager.checkFriend(session.getUserId(), friendId);

        // 互相解除好友关系，走代理清理缓存
        friendManager.unbindFriend(session.getUserId(), friendId);
        friendManager.unbindFriend(friendId, session.getUserId());

        //给自己的其他端发送信息
        //构造 PrivateMessageVO
        PrivateMessageVO privateMessageVO = new PrivateMessageVO();
        privateMessageVO.setId(idGenerator.getMsgId());
        privateMessageVO.setSendId(session.getUserId());
        privateMessageVO.setRecvId(session.getUserId());
        privateMessageVO.setType(MessageType.DEL_FRIEND.code());
        privateMessageVO.setContent(friendId.toString());
        privateMessageVO.setSendTime(new Date());

        IMMessage<PrivateMessageVO> imPrivateMessage =
                new IMMessage<>(privateMessageVO.getId(), privateMessageVO.getSeqId(), Command.PRIVATE_OUT_MESSAGE, true);
        imPrivateMessage.setRecvIds(Collections.singletonList(session.getUserId()));
        imPrivateMessage.setData(privateMessageVO);

        imClient.sendMessage(imPrivateMessage);

        log.info("删除好友，用户id:{},好友id:{}", session.getUserId(), friendId);
    }

    /**
     * 查找好友
     *
     * @param friendId 好友的ID
     * @return 好友信息
     */
    @Override
    public FriendVO findFriend(Long friendId) {
        UserSession session = SessionContext.getSession();

        Friend friend = friendManager.findFriend(session.getUserId(), friendId);

        FriendVO vo = new FriendVO();
        vo.setId(friend.getFriendId());
        vo.setHeadImage(friend.getFriendHeadImage());
        vo.setNickName(friend.getFriendNickName());

        return vo;
    }

    /**
     * 申请添加好友
     */
    @Override
    public void applyAddFriend(AddFriendDTO dto) {
        UserSession session = SessionContext.getSession();
        //检查是不是好友、是不是黑名单
        //TODO
        //发送申请
        User user = userManager.getById(dto.getUserId());

        UserVO userVO = UserVO.builder().sex(user.getSex())
                .headImageThumb(user.getHeadImageThumb())
                .nickName(user.getNickName())
                .signature(user.getSignature())
                .build();

        //基于自己的userId构造临时token
        ApplyFriendTemp applyFriendTemp = new ApplyFriendTemp();
        applyFriendTemp.setOwner(session.getUserId());
        applyFriendTemp.setUser(dto.getUserId());
        String tempToken = JwtUtil.createTempToken(
                "addFriend",
                dto.getUserId().toString(),
                applyFriendTemp,
                200);

        //构造消息内容
        ApplyFriendVO applyFriendVO = new ApplyFriendVO();
        applyFriendVO.setTempToken(tempToken);
        applyFriendVO.setUser(userVO);
        applyFriendVO.setApplyContent(dto.getApplyContent());

        //构造 PrivateMessageVO
        PrivateMessageVO privateMessageVO = new PrivateMessageVO();
        privateMessageVO.setId(idGenerator.getMsgId());
        privateMessageVO.setSendId(session.getUserId());
        privateMessageVO.setRecvId(dto.getUserId());
        privateMessageVO.setType(MessageType.APPLY_ADD_FRIEND.code());
        privateMessageVO.setContent(JSON.toJSONString(applyFriendVO));
        privateMessageVO.setSendTime(new Date());

        IMMessage<PrivateMessageVO> imPrivateMessage =
                new IMMessage<>(privateMessageVO.getId(), privateMessageVO.getSeqId(), Command.PRIVATE_OUT_MESSAGE, true);

        imPrivateMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
        imPrivateMessage.setRecvIds(Collections.singletonList(dto.getUserId()));
        imPrivateMessage.setData(privateMessageVO);

        imClient.sendMessage(imPrivateMessage);
    }

    /**
     * 处理好友申请
     * 1. 去redis查看申请是否过期
     * 2. 如果是统一申请，则将好友关系入库
     * 3. 给对方回执
     *
     * @param dto 请求参数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void handlerFriendApply(HandlerAddFriendDTO dto) {
        UserSession session = SessionContext.getSession();

        //解析临时token
        ApplyFriendTemp applyFriendTemp = JwtUtil.parseAndCheckTempToken(
                "addFriend",
                session.getUserId().toString(),
                dto.getTempToken(),
                ApplyFriendTemp.class);

        if (Objects.isNull(applyFriendTemp)) {
            throw new GlobalException(ResultEnum.NO_PERMISSION);
        }

        //验证token有效性
        if (!applyFriendTemp.getUser().equals(session.getUserId()) || !applyFriendTemp.getOwner().equals(dto.getTargetUserId())) {
            throw new GlobalException(ResultEnum.NO_PERMISSION);
        }

        Long friendId = dto.getTargetUserId();
        Boolean isAgree = dto.getIsAccept();

        if (isAgree) {
            //添加好友逻辑入库
            friendManager.bindFriend(session.getUserId(), friendId);
            friendManager.bindFriend(friendId, session.getUserId());
            log.info("添加好友，用户id:{},好友id:{}", session.getUserId(), friendId);
        }

        //发送回执消息
        User user = userManager.getById(session.getUserId());

        UserVO userVO = UserVO.builder().sex(user.getSex())
                .headImageThumb(user.getHeadImageThumb())
                .nickName(user.getNickName())
                .signature(user.getSignature())
                .build();

        HandlerFriendApplyVO handlerFriendApplyVO = new HandlerFriendApplyVO();
        handlerFriendApplyVO.setHandlerUser(userVO);
        handlerFriendApplyVO.setIsAccept(isAgree);
        handlerFriendApplyVO.setContent(dto.getContent());

        //构造 PrivateMessageVO
        PrivateMessageVO privateMessageVO = new PrivateMessageVO();
        privateMessageVO.setId(idGenerator.getMsgId());
        privateMessageVO.setSendId(session.getUserId());
        privateMessageVO.setRecvId(friendId);

        privateMessageVO.setType(isAgree ? MessageType.AGREE_FRIEND_APPLY.code() : MessageType.REFUSE_FRIEND_APPLY.code());
        privateMessageVO.setContent(JSON.toJSONString(handlerFriendApplyVO));
        privateMessageVO.setSendTime(new Date());

        IMMessage<PrivateMessageVO> imPrivateMessage =
                new IMMessage<>(privateMessageVO.getId(), privateMessageVO.getSeqId(), Command.PRIVATE_OUT_MESSAGE, true);

        imPrivateMessage.setRecvIds(Collections.singletonList(friendId));
        imPrivateMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
        imPrivateMessage.setData(privateMessageVO);

        imClient.sendMessage(imPrivateMessage);

    }

    @Override
    public List<FriendVO> findFriendList() {
        List<Friend> friends = friendManager.findFriendByUserId(SessionContext.getSession().getUserId());
        List<FriendVO> vos = friends.stream().map(f -> {
            FriendVO vo = new FriendVO();
            vo.setId(f.getFriendId());
            vo.setHeadImage(f.getFriendHeadImage());
            vo.setNickName(f.getFriendNickName());
            return vo;
        }).collect(Collectors.toList());
        return vos;
    }

    @Override
    public List<OnlineTerminalVO> getFriendsOnlineTerminals() {
        UserSession session = SessionContext.getSession();
        List<Friend> friendList = friendManager.findFriendByUserId(session.getUserId());

        if (friendList.isEmpty()) {
            return Collections.emptyList();
        }

        //TODO 只查询活跃用户，不活跃的不查询
        List<Long> friendIds = friendList.stream().map(Friend::getFriendId).collect(Collectors.toList());

        Map<Long, List<IMTerminalType>> onlineTerminal = imClient.getOnlineTerminal(friendIds);

        // 组装vo
        List<OnlineTerminalVO> vos = new LinkedList<>();
        onlineTerminal.forEach((userId, types) -> {
            List<Integer> terminals = types.stream().map(IMTerminalType::code).collect(Collectors.toList());
            vos.add(new OnlineTerminalVO(userId, terminals));
        });

        return vos;

    }

    /**
     * 思路是伪装私聊消息，此消息的MessageType是“上线”，告知对方我已上线
     *
     * @param recvIds
     */
    @Override
    public void pushMyOnline(List<Long> recvIds) {

        UserSession session = SessionContext.getSession();

        // 推送消息
        PrivateMessageVO privateMessageVO = new PrivateMessageVO();
        privateMessageVO.setId(idGenerator.getMsgId());
        privateMessageVO.setSendId(session.getUserId());

        privateMessageVO.setContent("你上线了");
        privateMessageVO.setSendTime(new Date());
        privateMessageVO.setType(MessageType.ONLINE.code());
        privateMessageVO.setStatus(MessageStatus.NORMAL.code());
        privateMessageVO.setSendId(session.getUserId());

        IMMessage<PrivateMessageVO> imPrivateMessage =
                new IMMessage<>(privateMessageVO.getId(), privateMessageVO.getSeqId(), Command.PRIVATE_OUT_MESSAGE, true);

        imPrivateMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
        imPrivateMessage.setSendToSelf(false);
        imPrivateMessage.setData(privateMessageVO);
        //如果用户离线，直接丢弃，不需要进入离线逻辑
        imPrivateMessage.setIsNeedOfflineStore(false);

        for (Long recvId : recvIds) {
            privateMessageVO.setRecvId(recvId);
            imPrivateMessage.setRecvIds(recvIds);
            //此消息不需要持久化，也不需要进入离线消息逻辑
            imClient.sendMessage(imPrivateMessage);
        }
    }
}
